//
// Created on 6/1/25.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "loader.h"
#include "geometry.h"
#include "graphdata.h"
#include "tinyobj.h"
#include "glm/glm.hpp"

#include <resourcemanager/ohresmgr.h>
Loader::Loader() {
//    NativeResourceManager *manager = OH_ResourceManager_InitNativeResourceManager(env, args[0]);
}
Loader::~Loader() {}

std::shared_ptr<Geometry> Loader::load(std::string filename) {
    tinyobj::attrib_t attrib;
    std::vector<tinyobj::shape_t> shapes;
    std::vector<tinyobj::material_t> materials;

    std::string warn;
    std::string err;


    std::shared_ptr<Geometry> geometry = std::make_shared<Geometry>();
    std::shared_ptr<VertexProp> vertex = std::make_shared<VertexProp>();
    std::shared_ptr<PropertyType<float>> position = std::make_shared<PropertyType<float>>();
    std::shared_ptr<PropertyType<float>> normal = std::make_shared<PropertyType<float>>();
    bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename.c_str());
    float minx = 99999, miny = 99999, minz = 99999;
    float maxx = -minx, maxy = -miny, maxz = -minz;
    if (!shapes.size())
        return geometry;
    for (int i = 0; i < shapes[0].mesh.indices.size(); i++) {
        tinyobj::index_t index = shapes[0].mesh.indices[i];

        float valuex = attrib.vertices[index.vertex_index * 3 + 0]*m_scale;
        float valuey = attrib.vertices[index.vertex_index * 3 + 1]*m_scale;
        float valuez = attrib.vertices[index.vertex_index * 3 + 2]*m_scale;
        position->addItem(valuex);
        position->addItem(valuey);
        position->addItem(valuez);
        normal->addItem(attrib.normals[index.normal_index * 3 + 0]);
        normal->addItem(attrib.normals[index.normal_index * 3 + 1]);
        normal->addItem(attrib.normals[index.normal_index * 3 + 2]);
        if (1) {
            maxx = maxx < valuex ? valuex : maxx;
            minx = minx > valuex ? valuex : minx;
        }
        if (1) {
            maxy = maxy < valuey ? valuey : maxy;
            miny = miny > valuey ? valuey : miny;
        }
        if (1) {
            maxz = maxz < valuez ? valuez : maxz;
            minz = minz > valuez ? valuez : minz;
        }
    }

    vertex->AddProperty(Property::Vertex, position);
    vertex->AddProperty(Property::Normal, normal);
    geometry->SetVertex(vertex);
    geometry->SetBBox(glm::vec3(minx, miny, minz), glm::vec3(maxx, maxy, maxz));
    geometry->SetPrimitiveType(PrimitiveType::TRIANGLES);
    return geometry;
}

SWCLoader::SWCLoader() {}
SWCLoader::~SWCLoader() {
    
}

std::shared_ptr<Geometry> createSoma(glm::vec3 center,float ratio=1.0f){
    //Creat Vertex
    const float m_alphaMax = 360;
    const float m_betaMax = 180;
    const float m_PI = 3.14159265359f;
    const float m_ratio=ratio;
    int segment=100;
    std::shared_ptr<PropertyType<glm::vec3>> position0 = std::make_shared<PropertyType<glm::vec3>>();
    std::shared_ptr<PropertyType<glm::vec3>> normal0 = std::make_shared<PropertyType<glm::vec3>>();
    
    for (int i = 0; i < segment + 1; i++)
    {
        float beta = m_betaMax / segment*i;
        float yPos = glm::cos(glm::radians(beta))*m_ratio;
        for (int j = 0; j < segment + 1; j++)
        {
            float alpha = m_alphaMax / segment*j;
            float xPos = m_ratio*glm::sin(glm::radians(beta))*glm::cos(glm::radians(alpha));
            float zPos = m_ratio*glm::sin(glm::radians(beta))*glm::sin(glm::radians(alpha));
            position0->addItem(glm::vec3(xPos, yPos, zPos));
            normal0->addItem(glm::vec3(xPos, yPos, zPos));
        }
    }
    std::shared_ptr<PropertyType<glm::vec3>> position = std::make_shared<PropertyType<glm::vec3>>();
    std::shared_ptr<PropertyType<glm::vec3>> normal = std::make_shared<PropertyType<glm::vec3>>();
    std::shared_ptr<PropertyType<glm::vec3>> centerP = std::make_shared<PropertyType<glm::vec3>>();
    
     for (int i = 0; i < segment; i++)
    {
        for (int j = 0; j < segment; j++)
        {
            unsigned int point0 = i*(segment + 1) + j;
            unsigned int point1 = i*(segment + 1) + j + 1;
            unsigned int point2 = (i + 1)*(segment + 1) + j;
            unsigned int point3 = (i + 1)*(segment + 1) + j + 1;


            position->addItem(position0->m_Data[point0]);
            position->addItem(position0->m_Data[point1]);
            position->addItem(position0->m_Data[point2]);
            position->addItem(position0->m_Data[point1]);
            position->addItem(position0->m_Data[point2]);
            position->addItem(position0->m_Data[point3]);
            
            normal->addItem(normal0->m_Data[point0]);
            normal->addItem(normal0->m_Data[point1]);
            normal->addItem(normal0->m_Data[point2]);
            normal->addItem(normal0->m_Data[point1]);
            normal->addItem(normal0->m_Data[point2]);
            normal->addItem(normal0->m_Data[point3]);
        }
    }
    centerP->addItem(center);
    std::shared_ptr<Geometry> geometry = std::make_shared<Geometry>();
    std::shared_ptr<VertexProp> vertex = std::make_shared<VertexProp>();
    vertex->AddProperty(Property::Vertex, position);
    vertex->AddProperty(Property::Normal, normal);
    vertex->AddProperty(Property::Other, centerP);
    geometry->SetVertex(vertex);
    return geometry;
}
std::shared_ptr<Geometry> SWCLoader::load(std::string filename) {
//    std::shared_ptr<GraphPriv> graphdata = std::make_shared<GraphPriv>();
    Graph graph = GraphPriv::alloc();
    auto gp = GraphPriv::get(graph);
    gp->fromSwc(filename.c_str());
    
    if (gp->trees.size() == 0)
        return nullptr;
    
    std::shared_ptr<Geometry> geometry = std::make_shared<Geometry>();
    std::shared_ptr<VertexProp> vertex = std::make_shared<VertexProp>();
    std::shared_ptr<PropertyType<glm::vec3>> position = std::make_shared<PropertyType<glm::vec3>>();
    vertex->AddProperty(Property::Vertex, position);
    geometry->SetVertex(vertex);
    glm::vec3 center = glm::vec3(gp->trees[0].root().point().x(),gp->trees[0].root().point().y(),gp->trees[0].root().point().z());
    auto soma =createSoma(center*m_scale);
    geometry->AddChild(soma);
    
    for (auto point : gp->trees[0].root().neighbors()) {
        if (point.first) {
            auto edge = gp->edges[point.first.index()];
            Vertex vertex = edge.leftVertex() == gp->trees[0].root() ? edge.rightVertex() : edge.leftVertex();
            if (vertex) {
                // get neighbors
                CreateSingleNode(position, &vertex);
            }
        }
    }
		if (graph) {
            for (auto e : graph.edges()) {
                EdgePriv::free(e);
            }
	        GraphPriv::free(graph);
		}
    return geometry;
}

void SWCLoader::CreateSingleNode(std::shared_ptr<PropertyType<glm::vec3>> position, Vertex *v) {
    for (auto edge : v->neighbors()) {
        if (std::find(m_EdgeIndex.begin(), m_EdgeIndex.end(), edge.first.index()) != m_EdgeIndex.end())
            continue;
        auto points = edge.first.points();
        int i=0;int step=10;
        Point ver,vern;
        for (i = 0; i+step < points.size() - 1; i+=step) {
            ver = points[i];
            vern = points[i + step];
            position->addItem({ver.x()*m_scale, ver.y()*m_scale, ver.z()*m_scale});
            position->addItem({vern.x()*m_scale, vern.y()*m_scale, vern.z()*m_scale});
        }
        if(i==0){
            vern=points[0];
        }
            position->addItem({vern.x()*m_scale, vern.y()*m_scale, vern.z()*m_scale});
            position->addItem({points[points.size()-1].x()*m_scale, points[points.size()-1].y()*m_scale, points[points.size()-1].z()*m_scale});

        
//        position->addItem({points[points.size() - 1].x(), points[points.size() - 1].y(), points[points.size() - 1].z()});
        m_EdgeIndex.push_back(edge.first.index());
        if (edge.first.rightVertex() != edge.first.tree().root()) {
            auto rightV = edge.first.rightVertex();
            CreateSingleNode(position, &rightV);
        }
        if (edge.first.leftVertex() != edge.first.tree().root()) {
            auto leftV = edge.first.leftVertex();
            CreateSingleNode(position, &leftV);
        }
    }
}